package com.example.sanguogameserver.service;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.RandomUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.sanguogameserver.ai.enums.TreeType;
import com.example.sanguogameserver.ai.logic.AiLogic;
import com.example.sanguogameserver.ai.model.Position;
import com.example.sanguogameserver.dto.PlayerGeneralStartSumDTO;
import com.example.sanguogameserver.dto.battle.BattleTreeInfoColumn;
import com.example.sanguogameserver.dto.battle.BattleTreeInfoRow;
import com.example.sanguogameserver.entity.*;
import com.example.sanguogameserver.enums.MutationTypeEnum;
import com.example.sanguogameserver.enums.TreePlayerTypeEnum;
import com.example.sanguogameserver.mapper.PlayerGeneralMapper;
import com.example.sanguogameserver.request.*;
import com.example.sanguogameserver.response.*;
import com.example.sanguogameserver.util.AuthUtil;
import com.example.sanguogameserver.util.LevelUtil;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

/**
 * <p>
 * 玩家武将 服务实现类
 * </p>
 *
 * @author baomidou
 * @since 2024-03-15
 */
@Service
public class PlayerGeneralService extends ServiceImpl<PlayerGeneralMapper, PlayerGeneral> implements IService<PlayerGeneral> {

    @Autowired
    private GeneralService generalService;

    @Autowired
    private GeneralTypeService generalTypeService;

    @Autowired
    private PlayerService playerService;


    @Autowired
    private LevelUtil levelUtil;

    @Autowired
    private PlayerItemService playerItemService;
    @Autowired
    @Lazy
    private TerritoryService territoryService;
    @Autowired
    private ItemService itemService;

    @Autowired
    private SkillService skillService;
    @Autowired
    private SkillPlayerGeneralService skillPlayerGeneralService;

    BigDecimal minCoefficient = BigDecimal.valueOf(0.7);
    BigDecimal maxCoefficient = BigDecimal.valueOf(1.2);
    Integer fragmentMax = 10;
    Integer mutationMax = 10000;
    BigDecimal mutationCoefficient = BigDecimal.valueOf(2);

    @Transactional
    public RaffleResp raffle(RaffleRequest raffleRequest) {


        Player player = playerService.getById(AuthUtil.getCurrPlayerId());

        if (raffleRequest.getCount() == 1 && player.getResourcesTotal() < 2000) {
            throw new IllegalArgumentException("粮草不够");
        }

        if (raffleRequest.getCount() == 5 && player.getResourcesTotal() < 9900) {
            throw new IllegalArgumentException("粮草不够");
        }

        if (raffleRequest.getCount() == 1) {
            player.setResourcesTotal(player.getResourcesTotal() - 2000);
        }

        if (raffleRequest.getCount() == 10) {
            player.setResourcesTotal(player.getResourcesTotal() - 9900);
        }

        playerService.updateById(player);

        playerService.setRedisPlayerInfoToken(player, AuthUtil.getToken());


        // 获取所有武将
        List<General> generalAll = generalService.list();
        List<General> generals = RandomUtil.randomEles(generalAll, raffleRequest.getCount());
        Integer maxStar = 1;

        List<PlayerGeneral> playerGenerals = new ArrayList<>();
        List<PlayerItem> playerItems = new ArrayList<>();
        for (General general : generals) {
            if (isGeneral()) {
                PlayerGeneral playerGeneral = new PlayerGeneral();


                Integer star = rafflePlayerGeneral(general, playerGeneral, isMutation());
                if (star > maxStar) {
                    maxStar = star;
                }
                playerGenerals.add(playerGeneral);
            } else {
                Item fragmentItem = itemService.getByGeneralFragment(general.getId());

                PlayerItem playerItem = new PlayerItem();
                BeanUtils.copyProperties(fragmentItem, playerItem);
                playerItem.setId(null);
                playerItem.setPlayerId(AuthUtil.getCurrPlayerId());
                playerItem.setItemId(fragmentItem.getId());
                playerItem.setItemCount(1);
                playerItem.setCreateDate(LocalDateTime.now());

                playerItems.add(playerItem);
            }
        }

        for (PlayerGeneral playerGeneral : playerGenerals) {
            // 抽取技能
            save(playerGeneral);
            Skill skill = skillService.raffleSkill();
            SkillPlayerGeneral skillPlayerGeneral = new SkillPlayerGeneral();
            BeanUtils.copyProperties(skill, skillPlayerGeneral);
            skillPlayerGeneral.setId(null);
            skillPlayerGeneral.setPlayerGeneralId(playerGeneral.getId());
            skillPlayerGeneralService.save(skillPlayerGeneral);
        }
        playerItemService.savePlayerItemBatch(playerItems);
        RaffleResp raffleResp = new RaffleResp();
        raffleResp.setMaxStar(maxStar);
        raffleResp.setPlayerGenerals(playerGenerals);
        raffleResp.setPlayerItems(playerItems);

        return raffleResp;
    }

    public boolean isGeneral() {
        return RandomUtil.randomInt(1, fragmentMax) == RandomUtil.randomInt(1, fragmentMax);
    }

    public boolean isMutation() {
        return RandomUtil.randomInt(1, mutationMax) == RandomUtil.randomInt(1, mutationMax);
    }

    public Integer rafflePlayerGeneral(General general, PlayerGeneral playerGeneral, boolean isMutation) {

        BigDecimal healthCoefficient = RandomUtil.randomBigDecimal(minCoefficient, maxCoefficient);
        BigDecimal mpCoefficient = RandomUtil.randomBigDecimal(minCoefficient, maxCoefficient);
        BigDecimal attackCoefficient = RandomUtil.randomBigDecimal(minCoefficient, maxCoefficient);
        BigDecimal defenseCoefficient = RandomUtil.randomBigDecimal(minCoefficient, maxCoefficient);
        BigDecimal spellStrengthCoefficient = RandomUtil.randomBigDecimal(minCoefficient, maxCoefficient);
        BigDecimal dodgeCoefficient = RandomUtil.randomBigDecimal(minCoefficient, maxCoefficient);
        BigDecimal critCoefficient = RandomUtil.randomBigDecimal(minCoefficient, maxCoefficient);

        BigDecimal gainCoefficient = new BigDecimal(0);
        if (isMutation) {
            gainCoefficient = mutationCoefficient;
        }
        playerGeneral.setPlayId(AuthUtil.getCurrPlayerId());
        playerGeneral.setGeneralId(general.getId());
        playerGeneral.setGeneralCategory(general.getGeneralCategory());
        playerGeneral.setGeneralName((isMutation ? "变异的" : "") + general.getGeneralName());
        playerGeneral.setMutationType(isMutation ? MutationTypeEnum.dark : MutationTypeEnum.ordinary);
        playerGeneral.setHealth(healthCoefficient.multiply(BigDecimal.valueOf(general.getHealth())).intValue());
        playerGeneral.setHealthIncreaseRate(healthCoefficient.add(gainCoefficient));
        playerGeneral.setMp(mpCoefficient.multiply(BigDecimal.valueOf(general.getMp())).intValue());
        playerGeneral.setMpIncreaseRate(mpCoefficient.add(gainCoefficient));
        playerGeneral.setAttack(Math.max(attackCoefficient.multiply(BigDecimal.valueOf(general.getAttack())).intValue(), 1));
        playerGeneral.setAttackIncreaseRate(attackCoefficient.add(gainCoefficient));
        playerGeneral.setDefense(Math.max(defenseCoefficient.multiply(BigDecimal.valueOf(general.getDefense())).intValue(), 1));
        playerGeneral.setDefenseIncreaseRate(defenseCoefficient.add(gainCoefficient));
        playerGeneral.setSpellStrength(Math.max(spellStrengthCoefficient.multiply(BigDecimal.valueOf(general.getSpellStrength())).intValue(), 1));
        playerGeneral.setSpellStrengthIncreaseRate(spellStrengthCoefficient.add(gainCoefficient));
        playerGeneral.setDodge(Math.max(dodgeCoefficient.multiply(BigDecimal.valueOf(general.getDodge())).intValue(), 1));
        playerGeneral.setDodgeIncreaseRate(dodgeCoefficient.add(gainCoefficient));
        playerGeneral.setCrit(Math.max(critCoefficient.multiply(BigDecimal.valueOf(general.getCrit())).intValue(), 1));
        playerGeneral.setCritIncreaseRate(critCoefficient.add(gainCoefficient));

        BigDecimal sumCoefficient = healthCoefficient.add(mpCoefficient).add(attackCoefficient).add(defenseCoefficient).add(spellStrengthCoefficient).add(dodgeCoefficient).add(critCoefficient);
        BigDecimal growthCoefficient = sumCoefficient.divide(BigDecimal.valueOf(7), 2, RoundingMode.HALF_UP);
        playerGeneral.setGrowthCoefficient(growthCoefficient);
        Integer star = transGrowCoefficientToStar(growthCoefficient);

        playerGeneral.setStar(star.toString());

        playerGeneral.setLevel(1);
        playerGeneral.setCurrentExp(0L);
        playerGeneral.setNextExp(levelUtil.getNextExp(playerGeneral.getLevel() + 1, isMutation, star));
        GeneralType initGeneral = generalTypeService.getInitGeneral(general.getGeneralCategory(), isMutation ? MutationTypeEnum.dark : MutationTypeEnum.ordinary);
        playerGeneral.setGeneralType(initGeneral.getName());
        return star;
    }

    public Integer transGrowCoefficientToStar(BigDecimal growthCoefficient) {
        if (NumberUtil.isGreater(growthCoefficient, BigDecimal.valueOf(1.1))) {
            return 5;
        }
        if (NumberUtil.isGreater(growthCoefficient, BigDecimal.valueOf(1.05))) {
            return 4;
        }
        if (NumberUtil.isGreater(growthCoefficient, BigDecimal.valueOf(1.0))) {
            return 3;
        }
        if (NumberUtil.isGreater(growthCoefficient, BigDecimal.valueOf(0.95))) {
            return 2;
        }
        return 1;
    }

    public Page<PlayerGeneralResp> pageRecord(PlayerGeneralPageReq playerGeneralPageReq) {
        Page<PlayerGeneralResp> page = Page.of(playerGeneralPageReq.getPage(), playerGeneralPageReq.getSize());

        return baseMapper.pageRecord(playerGeneralPageReq, page);
    }

    public PlayerGeneralResp detailById(Integer id) {
        PlayerGeneral playerGeneral = getById(id);
        General general = generalService.getById(playerGeneral.getGeneralId());
        GeneralType generalType = generalTypeService.getGeneralByCategoryAndName(playerGeneral.getGeneralCategory(), playerGeneral.getGeneralType());
        PlayerGeneralResp playerGeneralResp = new PlayerGeneralResp();
        BeanUtils.copyProperties(playerGeneral, playerGeneralResp);
        GeneralResp generalResp = new GeneralResp();
        BeanUtils.copyProperties(general, generalResp);
        GeneralTypeResp generalTypeResp = new GeneralTypeResp();
        BeanUtils.copyProperties(generalType, generalTypeResp);
        playerGeneralResp.setGeneral(generalResp);
        playerGeneralResp.setGeneralTypeResp(generalTypeResp);
        // 移动和攻击范围
        List<BattleTreeInfoRow> treeInfoRows = buildMoveAndAttackRange(generalTypeResp.getMoveCount(), generalTypeResp.getRange(), generalTypeResp.getRangeCount());
        playerGeneralResp.setMoveAndAttackRange(treeInfoRows);

        //技能
        List<SkillPlayerGeneral> skillPlayerGenerals = skillPlayerGeneralService.listByPlayerGeneralId(id);
        if (CollUtil.isNotEmpty(skillPlayerGenerals)) {
            List<String> skillCode = skillPlayerGenerals.stream().map(SkillPlayerGeneral::getSkillCode).toList();
            List<Skill> skills = skillService.getByCodes(skillCode);
            playerGeneralResp.setSkills(skills);
        }


        return playerGeneralResp;
    }

    private List<BattleTreeInfoRow> buildMoveAndAttackRange(int moveCount, String rangeJson, int rangeCount) {
        int row = (moveCount * 2) + 1;
        int column = (moveCount * 2) + 1;

        List<BattleTreeInfoRow> treeInfoRows = new ArrayList<>();
        for (int i = 0; i < row; i++) {
            BattleTreeInfoRow treeInfoRow = new BattleTreeInfoRow();
            treeInfoRow.setRow(i);
            List<BattleTreeInfoColumn> treeInfoColumns = new ArrayList<>();
            for (int j = 0; j < column; j++) {
                BattleTreeInfoColumn treeInfoColumn = new BattleTreeInfoColumn();
                treeInfoColumn.setTreeType(TreeType.grassland);
                treeInfoColumn.setIsMove(true);
                treeInfoColumn.setRow(i);
                treeInfoColumn.setColumn(j);
                if (moveCount == i && moveCount == j) {
                    treeInfoColumn.setPlayerRange(true);
                }
                treeInfoColumns.add(treeInfoColumn);
            }
            treeInfoRow.setTreeInfoColumns(treeInfoColumns);
            treeInfoRows.add(treeInfoRow);
        }

        AiLogic aiLogic = new AiLogic(treeInfoRows, TreePlayerTypeEnum.blue);

        Position currentposition = new Position();
        currentposition.setX(moveCount);
        currentposition.setY(moveCount);

        List<Position> positions = aiLogic.moveRange(currentposition, moveCount);
        for (Position position : positions) {
            treeInfoRows.get(position.y).getTreeInfoColumns().get(position.x).setMoveRange(true);
        }


        List<BattleTreeInfoColumn> attackPlayer = aiLogic.getAttackRange(rangeJson, rangeCount, treeInfoRows.get(moveCount).getTreeInfoColumns().get(moveCount));
        for (BattleTreeInfoColumn treeInfoColumn : attackPlayer) {
            treeInfoRows.get(treeInfoColumn.getRow()).getTreeInfoColumns().get(treeInfoColumn.getColumn()).setAttackRange(true);
        }
        return treeInfoRows;
    }

    public void updatePlayerName(Integer id, String nickName) {
        PlayerGeneral playerGeneral = getById(id);
        Assert.isTrue(playerGeneral.getPlayId().equals(AuthUtil.getCurrPlayerId()));
        playerGeneral.setGeneralName(nickName);
        updateById(playerGeneral);
    }


    public List<PlayerItem> exile(ExileReq exileReq) {
        List<PlayerItem> playerItems = new ArrayList<>();
        for (Integer id : exileReq.getIds()) {
            PlayerGeneral playerGeneral = getById(id);
            Assert.isTrue(playerGeneral.getPlayId().equals(AuthUtil.getCurrPlayerId()));
            removeById(id);
            Item fragmentItem = itemService.getByGeneralFragment(playerGeneral.getGeneralId());

            PlayerItem playerItem = new PlayerItem();
            BeanUtils.copyProperties(fragmentItem, playerItem);
            playerItem.setId(null);
            playerItem.setPlayerId(AuthUtil.getCurrPlayerId());
            playerItem.setItemId(fragmentItem.getId());
            playerItem.setItemCount(5);
            playerItem.setCreateDate(LocalDateTime.now());
            playerItems.add(playerItem);
        }
        playerItemService.savePlayerItemBatch(playerItems);
        // 获得五个碎片
        return playerItems;
    }

    public PlayerGeneralResp previousOrNext(PreviousOrNextReq orNextReq) {
        PlayerGeneralPageReq playerGeneralPageReq = new PlayerGeneralPageReq();
        List<Integer> ids = baseMapper.listAll(playerGeneralPageReq);
        int index = CollUtil.indexOf(ids, orNextReq);
        Integer id;
        if (orNextReq.isNext()) {
            index++;
        } else {
            index--;
        }
        if (index < 0) index = ids.size() - 1;
        if (index >= ids.size()) index = 0;
        id = ids.get(index);

        return detailById(id);

    }

    public PlayerGeneral synthesis(PlayerGeneralSynthesisReq playerGeneralSynthesisReq) {
        PlayerItem playerItem = playerItemService.getById(playerGeneralSynthesisReq.getPlayerItemId());
        Assert.isTrue(playerItem.getItemCount() >= 10, "变异碎片不足");
        playerItem.setItemCount(playerItem.getItemCount() - 10);
        playerItemService.updateById(playerItem);

        // 是否变异
        boolean mutation = isMutation();
        PlayerGeneral playerGeneral = new PlayerGeneral();
        General general = generalService.getById(playerItem.getGeneralId());
        rafflePlayerGeneral(general, playerGeneral, mutation);
        save(playerGeneral);

        Skill skill = skillService.raffleSkill();
        SkillPlayerGeneral skillPlayerGeneral = new SkillPlayerGeneral();
        BeanUtils.copyProperties(skill, skillPlayerGeneral);
        skillPlayerGeneral.setPlayerGeneralId(playerGeneral.getId());
        skillPlayerGeneralService.save(skillPlayerGeneral);
        return playerGeneral;
    }

    public PlayerGeneralStartSumDTO getPlayerGeneralStartSumDTO(Integer playerId) {
        return baseMapper.getPlayerGeneralStartSumDTO(playerId);
    }

    public List<PlayerGeneral> listUnTerrain(Integer playerId) {
        return baseMapper.selectUnTerrain(playerId);
    }

    @Transactional
    public void garrison(GarrisonReq garrisonReq) {
        Territory territory = territoryService.getById(garrisonReq.getTerrainId());
        Assert.isTrue(Objects.equals(AuthUtil.getCurrPlayerId(), territory.getPlayId()));
        if (garrisonReq.isCaptain()) {
            territory.setPlayerGeneralId(garrisonReq.getPlayerGeneralId());
            territoryService.updateById(territory);
        }

        PlayerGeneral playerGeneral = getById(garrisonReq.getPlayerGeneralId());
        Assert.isTrue(Objects.equals(AuthUtil.getCurrPlayerId(), playerGeneral.getPlayId()));
        playerGeneral.setTerritoryId(garrisonReq.getTerrainId());
        playerGeneral.setTerritoryIndex(garrisonReq.getTerritoryIndex());
        updateById(playerGeneral);
    }

    public TerrainAndUnTerrainResp listByTerrainAndUnTerrain(Integer territoryId) {
        TerrainAndUnTerrainResp terrainAndUnTerrainResp = new TerrainAndUnTerrainResp();
        List<PlayerGeneral> unTerrainPlayerGeneralResps = listUnTerrain(AuthUtil.getCurrPlayerId());
        terrainAndUnTerrainResp.setUnTerrainPlayer(unTerrainPlayerGeneralResps);
        List<PlayerGeneral> terrainPlayerGeneralResps = baseMapper.selectByTerritoryId(territoryId, AuthUtil.getCurrPlayerId());
        terrainAndUnTerrainResp.setTerrainPlayer(terrainPlayerGeneralResps);
        return terrainAndUnTerrainResp;
    }

    public void removeGarrison(GarrisonReq garrisonReq) {
        Territory territory = territoryService.getById(garrisonReq.getTerrainId());
        Assert.isTrue(Objects.equals(AuthUtil.getCurrPlayerId(), territory.getPlayId()));
        if (garrisonReq.isCaptain()) {
            territoryService
                    .lambdaUpdate()
                    .set(Territory::getPlayerGeneralId, null)
                    .eq(Territory::getId, territory.getId()).update();
        }

        PlayerGeneral playerGeneral = getById(garrisonReq.getPlayerGeneralId());
        Assert.isTrue(Objects.equals(AuthUtil.getCurrPlayerId(), playerGeneral.getPlayId()));
        lambdaUpdate()
                .set(PlayerGeneral::getTerritoryId, null)
                .set(PlayerGeneral::getTerritoryIndex, null)
                .eq(PlayerGeneral::getId, playerGeneral.getId()).update();
    }

    public void batchSetAttackTaskId(List<PlayerGeneralIdIndex> playerGeneralsId, Integer attackTaskId) {
        for (PlayerGeneralIdIndex playerGeneralId : playerGeneralsId) {
            PlayerGeneral playerGeneral = getById(playerGeneralId.getId());
            playerGeneral.setAttackTaskId(attackTaskId);
            playerGeneral.setAttackTaskIndex(playerGeneralId.getIndex());
            updateById(playerGeneral);
        }

    }

    public List<PlayerGeneral> getByTaskId(Integer taskId) {
        return lambdaQuery()
                .eq(PlayerGeneral::getAttackTaskId, taskId)
                .orderByAsc(PlayerGeneral::getAttackTaskIndex)
                .list();
    }

    public List<PlayerGeneral> getByTerritoryId(Integer territoryId) {
        return lambdaQuery()
                .eq(PlayerGeneral::getTerritoryId, territoryId)
                .list();
    }
}
